home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Developer Essentials / DTS Sample Code / System 7.0 Samples / MacShell / CtlHandler.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-12-04  |  16.3 KB  |  578 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:        MacShell
  5. ** File:        ctlhandler.c
  6. ** Written by:  Eric Soldan
  7. **
  8. ** Copyright © 1991 Apple Computer, Inc.
  9. ** All rights reserved.
  10. */
  11.  
  12. /* This code implements the new 7.0 human-interface standards for both
  13. ** TextEdit and List controls.  These standards include the following features:
  14. **
  15. ** 1) Tabbing between TextEdit and List controls within a window.
  16. ** 2) Displaying what item is active.  The active TextEdit item is indicated
  17. **    by either a blinking caret, or a selection range.
  18. ** 3) List positioning via the keyboard.  Entries on the keyboard automatically
  19. **    select and display the closest List item.  Also, the up and down arrows
  20. **    scroll through the list.
  21. ** 4) Window scrollbars are handled.
  22. */
  23.  
  24.  
  25.  
  26. /*****************************************************************************/
  27.  
  28.  
  29.  
  30. #include "MacShell.h"            /* Get the MacShell includes/typedefs, etc.    */
  31. #include "MacShellCommon.h"        /* Get the stuff in common with rez.        */
  32. #include "MacShell.protos"        /* Get the prototypes for MacShell.            */
  33.  
  34. #ifndef __CONTROLS__
  35. #include <Controls.h>
  36. #endif
  37.  
  38. #ifndef __CTLHANDLER__
  39. #include "CtlHandler.h"
  40. #endif
  41.  
  42. #ifndef __EVENTS__
  43. #include <Events.h>
  44. #endif
  45.  
  46. #ifndef __LISTCONTROL__
  47. #include "ListControl.h"
  48. #endif
  49.  
  50. #ifndef __OSUTILS__
  51. #include <OSUtils.h>
  52. #endif
  53.  
  54. #ifndef __PACKAGES__
  55. #include <Packages.h>
  56. #endif
  57.  
  58. #ifndef __TEXTEDITCONTROL__
  59. #include "TextEditControl.h"
  60. #endif
  61.  
  62.  
  63.  
  64. /*****************************************************************************/
  65.  
  66.  
  67.  
  68. static FileRecHndl    gFrHndl;
  69. static Rect            gScrollRct;
  70. static Boolean        gVert;
  71.  
  72. static pascal void    ScrollActionProc(ControlHandle scrollCtl, short part);
  73.  
  74.  
  75.  
  76. /*****************************************************************************/
  77.  
  78.  
  79.  
  80. static Handle        buttonProcs[3] = {nil, nil, nil};
  81.  
  82.  
  83.  
  84. /*****************************************************************************/
  85. /*****************************************************************************/
  86. /*****************************************************************************/
  87.  
  88.  
  89.  
  90. /* This function converts a control handle to a control number.  The function
  91. ** simply walks the window's control list and counts how many controls it
  92. ** has to traverse before finding the target control.  The smallest control
  93. ** number that can be returned is 1.  This makes control numbers similar to
  94. ** dialog item numbers. */
  95.  
  96. #pragma segment Controls
  97. short    Ctl2CNum(ControlHandle ctl)
  98. {
  99.     ControlHandle    nextCtl;
  100.     short            ctlNum;
  101.  
  102.     nextCtl = ((WindowPeek)(*ctl)->contrlOwner)->controlList;
  103.     for (ctlNum = 0;;) {
  104.         if (!nextCtl) break;
  105.         ++ctlNum;
  106.         if (ctl == nextCtl) break;
  107.         nextCtl = (*nextCtl)->nextControl;
  108.     }
  109.     return(ctlNum);
  110. }
  111.  
  112.  
  113.  
  114. /*****************************************************************************/
  115.  
  116.  
  117.  
  118. /* This function reactivates the last active TextEdit or List control for
  119. ** the indicated window. */
  120.  
  121. #pragma segment Controls
  122. void    DoCtlActivate(WindowPtr window)
  123. {
  124.     BeginContent(window);
  125.  
  126.     if (CLFindActive(window))        /* If the last item active was, */
  127.         CLWindActivate(window);        /* a List, then activate it.    */
  128.     else
  129.         CTEWindActivate(window);    /* Otherwise, activate last TextEdit control. */
  130.  
  131.     EndContent(window);
  132. }
  133.  
  134.  
  135.  
  136. /*****************************************************************************/
  137.  
  138.  
  139.  
  140. /* This function returns which kind of button the control is.  This does more
  141. ** than GetCVariant in that it makes sure that the control is actually a
  142. ** button.  It does this by comparing the defProc against the known defProc
  143. ** for the various button types.  For 7.0, there is only one defProc for all
  144. ** variants, but for pre-7.0, there is one defProc value for each variant.
  145. ** The method below handles either case. */
  146.  
  147. #pragma segment Controls
  148. short    GetButtonVariant(ControlHandle ctl)
  149. {
  150.     short            i;
  151.     Rect            dummy;
  152.     ControlHandle    dummyCtl;
  153.  
  154.     for (i = pushButProc; i <= radioButProc; ++i) {
  155.         if (!buttonProcs[i]) {
  156.             SetRect(&dummy, 0, 0, 0, 0);
  157.             dummyCtl = NewControl((*ctl)->contrlOwner, &dummy, (ConstStr255Param)"\p", 1, false, 0, 1, i, 0L);
  158.             if (dummyCtl) {
  159.                 buttonProcs[i] = (*dummyCtl)->contrlDefProc;
  160.                 DisposeControl(dummyCtl);
  161.             }
  162.         }
  163.         if ((*ctl)->contrlDefProc == buttonProcs[i]) return(GetCVariant(ctl));
  164.     }
  165.  
  166.     return(-1);
  167. }
  168.  
  169.  
  170.  
  171. /*****************************************************************************/
  172.  
  173.  
  174.  
  175. /* This function returns all the checkBox values into the dsignated array.
  176. ** The function walks the control list, and when it finds a checkBox control,
  177. ** it gets the control value and places it in the next position in the array.
  178. ** This allows a single call to retrieve all the checkBox values at once. */
  179.  
  180. #pragma segment Controls
  181. void    GetCheckBoxValues(WindowPtr window, Boolean checkBoxVal[])
  182. {
  183.     ControlHandle    nextCtl;
  184.     short            checkBoxIndx;
  185.  
  186.     nextCtl = ((WindowPeek)window)->controlList;
  187.     for (checkBoxIndx = 0;;) {
  188.         if (!nextCtl) return;
  189.         if (GetButtonVariant(nextCtl) == checkBoxProc)
  190.             checkBoxVal[checkBoxIndx++] = GetCtlValue(nextCtl);
  191.         nextCtl = (*nextCtl)->nextControl;
  192.     }
  193. }
  194.  
  195.  
  196.  
  197. /*****************************************************************************/
  198.  
  199.  
  200.  
  201. /* This function returns which radio button is selected for a particular
  202. ** family of radio buttons.  It finds the radio button of the target family
  203. ** with the lowest control number and it subtracts this number from the
  204. ** selected radio button of the same family.  This gives a relative radio
  205. ** button number as a return result.  This way the position of the family
  206. ** of radio buttons can change in the window and the return result is the
  207. ** same. */
  208.  
  209. #pragma segment Controls
  210. short    GetRadioButtonChoice(WindowPtr window, short famNum)
  211. {
  212.     ControlHandle    nextCtl;
  213.     short            ctlNum, firstInFam;
  214.  
  215.     nextCtl = ((WindowPeek)window)->controlList;
  216.     for (ctlNum = 0, firstInFam = -1;;) {
  217.         if (!nextCtl) return(-1);        /* If a proper radio button family was
  218.                                         ** passed in, this can't happen.  The
  219.                                         ** -1 is an error that indicates that
  220.                                         ** the requested family didn't exist,
  221.                                         ** or that there was no selected
  222.                                         ** radio of the requested family. */
  223.         ++ctlNum;
  224.         if (GetButtonVariant(nextCtl) == radioButProc) {
  225.             if (GetCRefCon(nextCtl) == famNum) {
  226.                 if (firstInFam == -1) firstInFam = ctlNum;
  227.                 if (GetCtlValue(nextCtl)) return(ctlNum - firstInFam);
  228.             }
  229.         }
  230.         nextCtl = (*nextCtl)->nextControl;
  231.     }
  232. }
  233.  
  234.  
  235.  
  236. /*****************************************************************************/
  237.  
  238.  
  239.  
  240. /* This function currently handles events for TextEdit, List, and button
  241. ** controls in a window.  It also handles the window scrollbars and
  242. ** scrolling of the window. */
  243.  
  244. #pragma segment Controls
  245. short    IsCtlEvent(WindowPtr window, EventRecord *event,
  246.                    ControlHandle *retCtl, short *action)
  247. {
  248.     RgnHandle        frameRgn;
  249.     TEHandle        te, teNext;
  250.     ListHandle        list, listNext;
  251.     ControlHandle    ctl, activeCtl, teCtl, listCtl, nextCtl;
  252.     short            what, part, ctlNum, key, modifiers;
  253.     Boolean            hitFrame, hitScrollBar;
  254.     Point            clickLoc;
  255.     CTEDataHndl        teData;
  256.  
  257.     *retCtl = nil;
  258.     *action = 0;
  259.  
  260.     if ((what = event->what) == mouseDown) {
  261.  
  262.         frameRgn = DoCalcFrameRgn(window);
  263.         hitFrame = PtInRgn(event->where, frameRgn);
  264.         DisposeRgn(frameRgn);
  265.         if (hitFrame) return(HandleScrollEvent(window, event, retCtl, action));
  266.             /* If window scrollbar is clicked on, handle the window scroll event. */
  267.  
  268.         BeginContent(window);
  269.         clickLoc = event->where;
  270.         GlobalToLocal(&clickLoc);
  271.  
  272.         CTECtlHit();        /* Clear CTECtl defProc's last hit CTECtl. */
  273.         CLCtlHit();        /* Clear CLCtl defProc's last hit CLCtl. */
  274.  
  275.         if (part = FindControl(clickLoc, window, &ctl)) {
  276.             hitScrollBar = IsScrollBar(ctl);
  277.                 /* The List controls and TextEdit controls may have scrollbars.
  278.                 ** Find out if a scrollbar was pressed, because it may belong
  279.                 ** to a TextEdit or List control. */
  280.  
  281.             ctlNum = Ctl2CNum(ctl);
  282.  
  283.             if ((hitScrollBar) || (CTECtlHit())) {
  284.                     /* This test is for speed.  CTEClick would find out if a TextEdit
  285.                     ** control handled the mouse click, but not as fast as we would
  286.                     ** like.  The above test determines if it is worth investigating. */
  287.  
  288.                 if (CTEClick(event)) {        /* If click belongs to a TextEdit control... */
  289.                     if (list = CLFindActive(window)) CLActivate(false, list);
  290.                         /* If old active control was a List control, deactivate it.
  291.                         ** The TextEdit control doesn't know how to do this. */
  292.                     *retCtl = ctl;
  293.                         /* Return the control of the TextEdit control. */
  294.                     EndContent(window);
  295.                     return(ctlNum);
  296.                 }
  297.             }
  298.  
  299.             if ((hitScrollBar) || (CLCtlHit())) {
  300.                     /* This test is for speed.  CLClick would find out if a List
  301.                     ** control handled the mouse click, but not as fast as we would
  302.                     ** like.  The above test determines if it is worth investigating. */
  303.  
  304.                 if (CLClick(event, action)) {        /* If click belongs to a List control... */
  305.                     if (te = CTEFindActive(window)) CTEActivate(false, te);
  306.                         /* If old active control was a TextEdit control, deactivate it.
  307.                         ** The List control doesn't know how to do this. */
  308.                     *retCtl = ctl;
  309.                         /* Return the control of the TextEdit control. */
  310.                     EndContent(window);
  311.                     return(ctlNum);
  312.                 }
  313.             }
  314.  
  315.             *action = 0;
  316.  
  317.             if (TrackControl(ctl, clickLoc, nil)) {        /* Handle button controls. */
  318.  
  319.                 switch(GetButtonVariant(ctl)) {
  320.                     case pushButProc:
  321.                         break;
  322.                     case checkBoxProc:
  323.                         SetCtlValue(ctl, GetCtlValue(ctl) ^ 1);        /* Toggle checkBox value. */
  324.                         break;
  325.                     case radioButProc:
  326.                         nextCtl = ((WindowPeek)window)->controlList;
  327.                             /* The below loop walks the control list for the window and
  328.                             ** finds radio buttons of the correct family number.  If
  329.                             ** the found radio button is the one that was clicked on,
  330.                             ** the value is set true, otherwise it is set false. */
  331.                         for (; nextCtl; nextCtl = (*nextCtl)->nextControl) {
  332.                             if (GetButtonVariant(nextCtl) == radioButProc)
  333.                                 if (GetCRefCon(nextCtl) == GetCRefCon(ctl))
  334.                                     SetCtlValue(nextCtl, (nextCtl == ctl));
  335.                         }
  336.                         break;
  337.                 }
  338.             }
  339.             *retCtl = ctl;
  340.             EndContent(window);
  341.             return(ctlNum);
  342.         }
  343.  
  344.         EndContent(window);
  345.         return(0);
  346.     }
  347.  
  348.     if ((what == keyDown) || (what == autoKey)) {        /* If event was keypress... */
  349.  
  350.         modifiers = event->modifiers;
  351.         if (modifiers & cmdKey) return(0);
  352.             /* Not our job to handle this one. */
  353.  
  354.         key = event->message & charCodeMask;
  355.  
  356.         if (key == 9) {        /* If tab... */
  357.  
  358.             teNext    = nil;
  359.             listNext  = nil;
  360.             activeCtl = nil;
  361.  
  362.             if (te   = CTEFindActive(window)) activeCtl = CTEViewFromTE(te);
  363.             if (list = CLFindActive(window))  activeCtl = CLViewFromList(list);
  364.                 /* Find what the active control is. */
  365.  
  366.             if (!(teCtl = CTENext(window, &teNext, activeCtl)))
  367.                 teCtl = CTENext(window, &teNext, nil);
  368.                     /* Find the next TextEdit control from the active control. */
  369.  
  370.             if (!(listCtl = CLNext(window, &listNext, activeCtl)))
  371.                 listCtl = CLNext(window, &listNext, nil);
  372.                     /* Find the next List control from the active control. */
  373.  
  374.             if ((!teNext) && (!listNext)) return(0);
  375.                 /* No TextEdit or List controls in window, so we are done. */
  376.  
  377.             if (!activeCtl) nextCtl = ((WindowPeek)window)->controlList;
  378.             else            nextCtl = (*activeCtl)->nextControl;
  379.                 /* At this point we probably have the following information:
  380.                 **   1) Active control.
  381.                 **   2) First TextEdit control after the active control.
  382.                 **   3) First List control after the active control.
  383.                 ** What we want to do is determine if the TextEdit control or
  384.                 ** the List control is closest to the active control.
  385.                 ** We may not have a currently active control, so in that case
  386.                 ** we will start at the beginning of the window control list. */
  387.  
  388.             for (;;) {
  389.                 if (!nextCtl) nextCtl = ((WindowPeek)window)->controlList;
  390.  
  391.                 if (nextCtl == teCtl) {        /* Activate a TextEdit control... */
  392.                     BeginContent(window);    /* Clip out window scrollbars and growIcon. */
  393.                     if (list) CLActivate(false, list);
  394.                     CTEActivate(true, teNext);
  395.                     teData = (CTEDataHndl)(*teCtl)->contrlData;
  396.                     if ((*teData)->mode & cteTabSelectAll)
  397.                         CTESetSelect(0, (*teNext)->teLength, teNext);
  398.                             /* If the "select all TextEdit text when tabbed into" bit is
  399.                             ** set, then do that very thing. */
  400.                     EndContent(window);        /* Remove the clipping. */
  401.                     return(Ctl2CNum(*retCtl = teCtl));
  402.                 }
  403.  
  404.                 if (nextCtl == listCtl) {
  405.                     BeginContent(window);    /* Clip out window scrollbars and growIcon. */
  406.                     if (te) CTEActivate(false, te);
  407.                     CLActivate(true, listNext);
  408.                     EndContent(window);        /* Remove the clipping. */
  409.                     return(Ctl2CNum(*retCtl = listCtl));
  410.                 }
  411.                 nextCtl = (*nextCtl)->nextControl;
  412.             }
  413.         }
  414.  
  415.         if (te = CTEFindActive(window)) {        /* If TextEdit control is active... */
  416.             BeginContent(window);
  417.             CTEKey(event);            /* Allow key to be processed by the TextEdit control. */
  418.             *action = 1;
  419.             EndContent(window);
  420.             return(Ctl2CNum(*retCtl = CTEViewFromTE(te)));
  421.         }
  422.  
  423.         if (list = CLFindActive(window)) {        /* If List control is active... */
  424.             BeginContent(window);
  425.             CLKey(event);
  426.             EndContent(window);
  427.             return(Ctl2CNum(*retCtl = CLViewFromList(list)));
  428.         }
  429.     }
  430.  
  431.     return(0);
  432. }
  433.  
  434.  
  435.  
  436. /*****************************************************************************/
  437.  
  438.  
  439.  
  440. #pragma segment Controls
  441. short    HandleScrollEvent(WindowPtr window, EventRecord *event,
  442.                           ControlHandle *retCtl, short *action)
  443. {
  444.     WindowPtr        oldPort;
  445.     Point            clickLoc;
  446.     short            part;
  447.     ControlHandle    ctl;
  448.     short            value, h, v;
  449.     RgnHandle        updateRgn;
  450.  
  451.     GetPort(&oldPort);
  452.     SetPort(window);
  453.     gScrollRct = window->portRect;
  454.  
  455.     SetOrigin(0, -16384);
  456.     clickLoc = event->where;
  457.     GlobalToLocal(&clickLoc);
  458.         /* Scrollbars for window are offset -16384 vertically.  Get a local
  459.         ** coordinate that corresponds to this negative space. */
  460.  
  461.     if (!(part = FindControl(clickLoc, window, &ctl))) {
  462.         SetOrigin(gScrollRct.left, gScrollRct.top);            /* Restore the origin. */
  463.         *retCtl = nil;
  464.         *action = 0;
  465.         SetPort(oldPort);
  466.         return(kScrollEvent);
  467.     }
  468.  
  469.     gFrHndl = (FileRecHndl)GetWRefCon(window);
  470.     if ((*gFrHndl)->fileState.hScroll) gScrollRct.right  -= 15;
  471.     if ((*gFrHndl)->fileState.vScroll) gScrollRct.bottom -= 15;
  472.  
  473.     gVert = (((*ctl)->contrlRect.right - (*ctl)->contrlRect.left) == 16);
  474.     switch (part) {
  475.         case inThumb:
  476.             value = GetCtlValue(ctl);
  477.             SetOrigin(0, -16384);
  478.             part = TrackControl(ctl, clickLoc, nil);
  479.             SetOrigin(gScrollRct.left, gScrollRct.top);            /* Restore the origin. */
  480.             if (part) {
  481.                 value -= GetCtlValue(ctl);
  482.                     /* Value now has CHANGE in position.  if position changed, scroll. */
  483.                 if (value) {
  484.                     h = v = 0;
  485.                     if (gVert) v = value;
  486.                     else       h = value;
  487.                     ScrollRect(&gScrollRct, h, v, updateRgn = NewRgn());
  488.                     InvalRgn(updateRgn);
  489.                     DisposeRgn(updateRgn);
  490.                 }
  491.             }
  492.             break;
  493.         default:
  494.             SetOrigin(0, -16384);
  495.             TrackControl(ctl, clickLoc, (ProcPtr)ScrollActionProc);
  496.             SetOrigin(gScrollRct.left, gScrollRct.top);            /* Restore the origin. */
  497.             break;
  498.     }
  499.  
  500.     AdjustScrollBars(window);
  501.  
  502.     SetPort(oldPort);
  503.     return(kScrollEvent);
  504. }
  505.  
  506.  
  507.  
  508. /*****************************************************************************/
  509. /*****************************************************************************/
  510.  
  511.  
  512.  
  513. #pragma segment Controls
  514. pascal void    ScrollActionProc(ControlHandle scrollCtl, short part)
  515. {
  516.     WindowPtr    window;
  517.     short        delta, value, h, v;
  518.     short        oldValue, max;
  519.     Point        org;
  520.     RgnHandle    updateRgn, contPart, framePart;
  521.     
  522.     GetPort(&window);
  523.  
  524.     if (part) {                        /* If it was actually in the control. */
  525.  
  526.         switch (part) {
  527.             case inUpButton:
  528.             case inDownButton:        /* One line. */
  529.                 delta = (gVert) ? (*gFrHndl)->fileState.vArrowVal : (*gFrHndl)->fileState.hArrowVal;
  530.                 break;
  531.             case inPageUp:            /* One page. */
  532.             case inPageDown:
  533.                 delta = (gVert) ? (*gFrHndl)->fileState.vPageVal : (*gFrHndl)->fileState.hPageVal;
  534.                 break;
  535.         }
  536.  
  537.         if ( (part == inUpButton) || (part == inPageUp) )
  538.             delta = -delta;        /* Reverse direction for an upper. */
  539.  
  540.         value = (oldValue = GetCtlValue(scrollCtl)) + delta;
  541.         if (value < 0) value = 0;
  542.         if (value > (max = GetCtlMax(scrollCtl))) value = max;
  543.  
  544.         if (value != oldValue) {
  545.  
  546.             SetCtlValue(scrollCtl, value);
  547.             SetOrigin(gScrollRct.left, gScrollRct.top);
  548.             h = oldValue - value;
  549.             v = 0;
  550.             if (gVert) {
  551.                 v = h;
  552.                 h = 0;
  553.             }
  554.  
  555.             ScrollRect(&gScrollRct, h, v, updateRgn = NewRgn());
  556.             InvalRgn(updateRgn);
  557.             DisposeRgn(updateRgn);
  558.  
  559.             DoUpdateSeparate(window, &contPart, &framePart);
  560.             if (framePart) DisposeRgn(framePart);
  561.             if (contPart) {
  562.                 CopyRgn(contPart, ((WindowPeek)window)->updateRgn);
  563.                 DisposeRgn(contPart);
  564.                 BeginUpdate(window);
  565.                 GetContentOrigin(window, &org);
  566.                 SetOrigin(org.h, org.v);
  567.                 DoImageDocument(gFrHndl);
  568.                 EndUpdate(window);
  569.             }
  570.  
  571.             SetOrigin(0, -16384);
  572.         }
  573.     }
  574. }
  575.  
  576.  
  577.  
  578.